#pragma once

#include "list.h"

template<typename list, size_t value, size_t carry>
struct multiplication_short_reversed
{
   typedef list_t<
      (list::head_ * value + carry) % BASE,
      typename multiplication_short_reversed<
         typename list::tail_,
         value,
         (list::head_ * value + carry) / BASE
      >::result_
   > result_;
};

template<size_t value, size_t carry>
struct multiplication_short_reversed<empty_list_t, value, carry>
{
   typedef list_t<
      carry % BASE,
      typename multiplication_short_reversed<
         empty_list_t,
         value,
         carry / BASE
      >::result_
   > result_;
};

template<size_t value>
struct multiplication_short_reversed<empty_list_t, value, 0>
{
   typedef empty_list_t result_;
};

template<typename list, size_t value>
struct multiplication_short
{
   typedef typename reverse<
      typename multiplication_short_reversed<
         typename reverse<
            list
         >::result_,
         value,
         0
      >::result_
   >::result_ result_;
};

template<typename first, typename second>
struct multiplication_reversed
{
   typedef typename addition_reversed<
      typename shift<
         typename multiplication_short_reversed<
            first,
            second::head_,
            0
         >::result_,
         (length<second>::result_ - 1)
      >::result_,
      typename multiplication_reversed<
         first,
         typename second::tail_
      >::result_,
      0
   >::result_ result_;
};

template<typename first>
struct multiplication_reversed<first, empty_list_t>
{
   typedef empty_list_t result_;
};

template<typename second>
struct multiplication_reversed<empty_list_t, second>
{
   typedef empty_list_t result_;
};

template<typename first, typename second>
struct multiplication
{
   typedef typename reverse<
      typename multiplication_reversed<
         typename reverse<
            first
         >::result_,
           second
      >::result_
   >::result_ result_;
};